/*******************************************************************************
*  file    : rpcclient.cpp
*  created : 18.08.2013
*  author  : Slyshyk Oleksiy (alexSlyshyk@gmail.com)
*******************************************************************************/

#include <xmlrpcclient.h>
#include <xmlrpcconv.h>
#include <QTimer>
#include <QSettings>
#include <QScopedPointer>
#include "rpcclient.hpp"

#include <qlogger.hpp>
namespace{
QLogger* l = QLoggerEngine::getLogger("RPC");

QScopedPointer<RpcClient> rpc_client;
}


RpcClient* RpcClient::instance()
{
    if(!rpc_client)
        {
            rpc_client.reset(new RpcClient);
            rpc_client->loadSettings();
        }
    return rpc_client.data();
}

void RpcClient::loadSettings()
{
    QSettings s;
    s.beginGroup("xml-rpc");
    this->address_ = s.value("server", QStringLiteral("127.0.0.1")).toString();
    this->port_    = s.value("port"  , 8080                       ).toUInt();
    s.endGroup();
    logInfo(l)<<QString(tr("Обновление параметров сервера.[%1:%2]"))
                .arg(this->address_).arg(this->port_);
}

RpcClient::RpcClient(QObject *parent) :
    QObject(parent),
    server_connected_(false)
{
    test_conn_timer_ = new QTimer(this);
    connect(test_conn_timer_, SIGNAL(timeout()), this, SLOT(testConnection()) );
    test_conn_timer_->start(10000);
    QTimer::singleShot(100, this, SLOT(testConnection()) );
}

void RpcClient::setServerState(bool st)
{
    static bool is_first = true;
    if(is_first)
        {
            server_connected_ = st;
            emit serverStateChanged(server_connected_);
            is_first = false;
        }
    if(server_connected_ != st)
        {
            server_connected_ = st;
            emit serverStateChanged(server_connected_);
        }
}

void RpcClient::testConnection()
{
    echo(QStringLiteral("TEST_CONNECTION"));
    getModemStatus();
}

RpcClient::~RpcClient()
{
}

void RpcClient::echo(const QVariant& v)
{
    XmlRpcClient* client= new XmlRpcClient( this->address_, this->port_ );
    connect( client, SIGNAL( dataReady( QVariant)), this, SLOT( onEcho(QVariant)) );
    connect( client, SIGNAL( error(QString) ),      this, SLOT( onError(QString)) );
    client->execute( "echo", QVariantList() << v );
}

void RpcClient::onEcho(const QVariant& v)
{
    //logInfo(l)<<QStringLiteral("Rpc ECHO:")<<v.toString();
    if(v.toString() == QStringLiteral("TEST_CONNECTION"))
        {
            setServerState(true);
        }
    this->sender()->deleteLater();
}

void RpcClient::getModemStatus()
{
    XmlRpcClient* client= new XmlRpcClient( this->address_, this->port_ );
    connect( client, SIGNAL( dataReady( QVariant)), this, SLOT( onModemStatus(QVariant)) );
    connect( client, SIGNAL( error(QString) ),      this, SLOT( onError(QString)) );
    client->execute( "modemStatus", QVariantList() );
}

void RpcClient::addMole(const QString& phone, const QString& name, const QString &passwd,
                        const QString& memo, const QString &bat_change)
{
    XmlRpcClient* client= new XmlRpcClient( this->address_, this->port_ );
    connect( client, SIGNAL( dataReady( QVariant)), this, SLOT( onAddMole(QVariant)) );
    connect( client, SIGNAL( error(QString) ),      this, SLOT( onError(QString)) );
    QVariantList v;
    v << phone << name << passwd << memo << bat_change;
    client->execute( "addMole", QVariantList()<<QVariant(v) );
}

void RpcClient::updateMole(const QString &phone, const QString &name, const QString &passwd,
                           const QString &memo, const QString &bat_change, int id)
{
    XmlRpcClient* client= new XmlRpcClient( this->address_, this->port_ );
    connect( client, SIGNAL( dataReady( QVariant)), this, SLOT( onAddMole(QVariant)) );
    connect( client, SIGNAL( error(QString) ),      this, SLOT( onError(QString)) );
    QVariantList v;
    v << phone << name << passwd << memo << bat_change << id;
    client->execute( "updateMole", QVariantList()<<QVariant(v) );
}

void RpcClient::getMoleList()
{
    XmlRpcClient* client= new XmlRpcClient( this->address_, this->port_ );
    connect( client, SIGNAL( dataReady( QVariant)), this, SLOT( onGetMoleList(QVariant)) );
    connect( client, SIGNAL( error(QString) ),      this, SLOT( onError(QString)) );
    client->execute( "getMoleList", QVariantList() );
}

void RpcClient::hideMoles(const QStringList &lst)
{
    XmlRpcClient* client= new XmlRpcClient( this->address_, this->port_ );
    connect( client, SIGNAL( dataReady( QVariant)), this, SLOT( onAddMole(QVariant)) );
    connect( client, SIGNAL( error(QString) ),      this, SLOT( onError(QString)) );
    QVariantList res;for(const QString& s: lst)res<<s;
    client->execute( "hideMoles", QVariantList()<<QVariant(res) );
}

void RpcClient::addSmsToOutgoing(int idx, const QString& txt)
{
    XmlRpcClient* client= new XmlRpcClient( this->address_, this->port_ );
    connect( client, SIGNAL( dataReady( QVariant)), this, SLOT( onAddSmsToOutgoing(QVariant)) );
    connect( client, SIGNAL( error(QString) ),      this, SLOT( onError(QString)) );
    client->execute( "addSmsToOutgoing", QVariantList()<<idx<<txt );
}

void RpcClient::sendInfoRequest(const QStringList& lst)
{
    XmlRpcClient* client= new XmlRpcClient( this->address_, this->port_ );
    connect( client, SIGNAL( dataReady( QVariant)), this, SLOT( onRequest(QVariant)) );
    connect( client, SIGNAL( error(QString) ),      this, SLOT( onError(QString)) );
    QVariantList res;for(const QString& s: lst)res<<s;
    client->execute( "sendInfoRequest", QVariantList()<<QVariant(res) );
}

void RpcClient::sendPosRequest(const QStringList& lst)
{
    XmlRpcClient* client= new XmlRpcClient( this->address_, this->port_ );
    connect( client, SIGNAL( dataReady( QVariant)), this, SLOT( onRequest(QVariant)) );
    connect( client, SIGNAL( error(QString) ),      this, SLOT( onError(QString)) );
    QVariantList res;for(const QString& s: lst)res<<s;
    client->execute( "sendPosRequest", QVariantList()<<QVariant(res) );
}

void RpcClient::sendModeRequest(const QStringList &lst, int mode)
{
    XmlRpcClient* client= new XmlRpcClient( this->address_, this->port_ );
    connect( client, SIGNAL( dataReady( QVariant)), this, SLOT( onRequest(QVariant)) );
    connect( client, SIGNAL( error(QString) ),      this, SLOT( onError(QString)) );
    QVariantList res;for(const QString& s: lst)res<<s;
    client->execute( "sendModeRequest", QVariantList()<<QVariant(res)<<QVariant(mode) );
}

void RpcClient::getLastIncoming(const QStringList& lst)
{
    XmlRpcClient* client= new XmlRpcClient( this->address_, this->port_ );
    connect( client, SIGNAL( dataReady( QVariant)), this, SLOT( onGetLastIncoming(QVariant)) );
    connect( client, SIGNAL( error(QString) ),      this, SLOT( onError(QString)) );
    QVariantList res;for(const QString& s: lst)res<<s;
    client->execute( "getLastInfo", QVariantList()<<QVariant(res) );
}

void RpcClient::getWorkingTasks(const QStringList &lst)
{
    XmlRpcClient* client= new XmlRpcClient( this->address_, this->port_ );
    connect( client, SIGNAL( dataReady( QVariant)), this, SLOT( onGetWorkingTasks(QVariant)) );
    connect( client, SIGNAL( error(QString) ),      this, SLOT( onError(QString)) );
    QVariantList res;for(const QString& s: lst)res<<s;
    client->execute( "getWorkedTasks", QVariantList()<<QVariant(res) );
}

void RpcClient::onModemStatus(const QVariant &s)
{
    QString ms = s.toString();
    if( ms != modem_status_ )
        {
            modem_status_ = ms;
            emit modemStatusChanged(modem_status_);
        }

    logInfo(l)<<modem_status_;
    this->sender()->deleteLater();
}

void RpcClient::onAddMole(const QVariant& v)
{
    if(v.toBool())
        {
            logInfo(l)<<tr("Изменен список устройств.");
            QTimer::singleShot(10,this, SLOT(getMoleList()));
        }
    else
        logError(l)<<tr("Ошибка при добавлении (обновлении) девайса.");
    this->sender()->deleteLater();
}

void RpcClient::onGetMoleList(const QVariant &v)
{
    if(v.type() == QVariant::List)
        {
            std::vector<MoleLine> moles;
            moles.reserve(v.toList().size());
            for(const QVariant& line: v.toList())
                {
                    MoleLine rec;
                    int cnt = (static_cast<int>(rec.size()) < line.toList().size()) ?
                                  rec.size() : line.toList().size();
                    for(int i = 0; i < cnt; ++i)
                        rec[i] = line.toList()[i].toString();
                    moles.push_back(rec);
                }
            mole_list_ = moles;
            emit moleListUpdated(mole_list_.size());
        }
    this->sender()->deleteLater();
}

void RpcClient::onAddSmsToOutgoing(const QVariant& v)
{
    if(v.toBool())
        logInfo(l)<<tr("SMS добавлена в список");

    this->sender()->deleteLater();
}

void RpcClient::onRequest(const QVariant &v)
{
    if(v.toBool())
        logInfo(l)<<tr("Запрос добавлен в очередь");

    this->sender()->deleteLater();
}

void RpcClient::onGetLastIncoming(const QVariant &v)
{
    QVariantMap fault = getFaultCode(v);
    if(fault.keys().size())
        {
            //Fault
            logError(l)<<fault["faultCode"].toInt()<<":"<<fault["faultString"].toString();
            return;
        }
    last_incoming_.clear();
    QVariantList data = v.toList();
    for(const QVariant& line : data)
        {
            IncomingLine l;
            QStringList items = line.toStringList();
            int size = std::min(static_cast<int>(l.size()),items.size());
            for(int i = 0; i < size; ++i)
                {
                    l[i] = items[i];
                }
            last_incoming_.push_back(l);
        }

    emit lastIncomingUpdated(last_incoming_.size());
}

void RpcClient::onGetWorkingTasks(const QVariant &v)
{
    QVariantMap fault = getFaultCode(v);
    if(fault.keys().size())
        {
            //Fault
            logError(l)<<fault["faultCode"].toInt()<<":"<<fault["faultString"].toString();
            return;
        }
    mole_tasks_.clear();
    QVariantList data = v.toList();
    for(const QVariant& line : data)
        {
            QStringList items = line.toStringList();
            if(items.size() >= 6) //FIXME: fix magik number
                {
                    //logDebug(l)<<"Task :"<<items.join(";");
                    MoleTask mt;
                    mt.setId            (items[0].toInt());
                    mt.setRequestNum    (items[1]);
                    mt.setRequestText   (items[2]);
                    mt.setAnsver        (items[3].toInt());
                    mt.setStartTime     (QDateTime::fromString(items[4],"yyyy-MM-dd hh:mm:ss"));
                    mt.setStatus        (items[5].toInt());

                    mole_tasks_.push_back(mt);
                }
        }

    emit workingTasksUpdated(mole_tasks_.size());
}

void RpcClient::onError(const QString& e)
{
    logError(l)<< e ;
    this->sender()->deleteLater();
    setServerState(false);
}
